home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / rhythmbox / plugins / upnp_coherence / MediaPlayer.py < prev    next >
Encoding:
Python Source  |  2009-04-07  |  18.5 KB  |  450 lines

  1. # Licensed under the MIT license
  2. # http://opensource.org/licenses/mit-license.php
  3.  
  4. # Copyright 2008, Frank Scholz <coherence@beebits.net>
  5.  
  6. import urllib
  7.  
  8. import rhythmdb
  9.  
  10. from coherence.upnp.core.soap_service import errorCode
  11. from coherence.upnp.core import DIDLLite
  12.  
  13. import coherence.extern.louie as louie
  14.  
  15. from coherence.extern.simple_plugin import Plugin
  16.  
  17. from coherence import log
  18.  
  19. TRACK_COUNT = 1000000
  20.  
  21. class RhythmboxPlayer(log.Loggable):
  22.  
  23.     """ a backend to the Rhythmbox
  24.  
  25.     """
  26.     logCategory = 'rb_media_renderer'
  27.  
  28.     implements = ['MediaRenderer']
  29.     vendor_value_defaults = {'RenderingControl': {'A_ARG_TYPE_Channel':'Master'},
  30.                              'AVTransport': {'A_ARG_TYPE_SeekMode':('ABS_TIME','REL_TIME')}}
  31.     vendor_range_defaults = {'RenderingControl': {'Volume': {'maximum':100}}}
  32.  
  33.     def __init__(self, device, **kwargs):
  34.         self.warning("__init__ RhythmboxPlayer %r", kwargs)
  35.         self.shell = kwargs['shell']
  36.         self.server = device
  37.  
  38.         self.player = None
  39.         self.metadata = None
  40.         self.name = "Rhythmbox on %s" % self.server.coherence.hostname
  41.  
  42.         self.player = self.shell.get_player()
  43.         louie.send('Coherence.UPnP.Backend.init_completed', None, backend=self)
  44.  
  45.         self.playing = False
  46.         self.state = None
  47.         self.duration = None
  48.         self.volume = 1.0
  49.         self.muted_volume = None
  50.         self.view = []
  51.         self.tags = {}
  52.  
  53.     def __repr__(self):
  54.         return str(self.__class__).split('.')[-1]
  55.  
  56.     def volume_changed(self, player, parameter):
  57.         self.volume = self.player.props.volume
  58.         if self.volume > 0:
  59.             rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id)
  60.             self.server.rendering_control_server.set_variable(rcs_id, 'Volume', self.volume*100)
  61.  
  62.     def playing_song_changed(self, player, entry):
  63.         if self.server != None:
  64.             connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id)
  65.         if entry == None:
  66.             self.update('STOPPED')
  67.             self.playing = False
  68.             #self.entry = None
  69.             self.metadata = None
  70.             self.duration = None
  71.         else:
  72.             self.id = self.shell.props.db.entry_get (entry, rhythmdb.PROP_ENTRY_ID)
  73.             bitrate = self.shell.props.db.entry_get(entry, rhythmdb.PROP_BITRATE) * 1024 / 8
  74.             # Duration is in HH:MM:SS format
  75.             seconds = self.shell.props.db.entry_get(entry, rhythmdb.PROP_DURATION)
  76.             hours = seconds / 3600
  77.             seconds = seconds - hours * 3600
  78.             minutes = seconds / 60
  79.             seconds = seconds - minutes * 60
  80.             self.duration = "%02d:%02d:%02d" % (hours, minutes, seconds)
  81.  
  82.             mimetype = self.shell.props.db.entry_get(entry, rhythmdb.PROP_MIMETYPE)
  83.             # This isn't a real mime-type
  84.             if mimetype == "application/x-id3":
  85.                 mimetype = "audio/mpeg"
  86.             size = self.shell.props.db.entry_get(entry, rhythmdb.PROP_FILE_SIZE)
  87.  
  88.             # create item
  89.             item = DIDLLite.MusicTrack(self.id + TRACK_COUNT)
  90.             item.album = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ALBUM)
  91.             item.artist = self.shell.props.db.entry_get(entry, rhythmdb.PROP_ARTIST)
  92.             item.genre = self.shell.props.db.entry_get(entry, rhythmdb.PROP_GENRE)
  93.             item.originalTrackNumber = str(self.shell.props.db.entry_get (entry, rhythmdb.PROP_TRACK_NUMBER))
  94.             item.title = self.shell.props.db.entry_get(entry, rhythmdb.PROP_TITLE) # much nicer if it was entry.title
  95.  
  96.             item.res = []
  97.  
  98.             uri = self.shell.props.db.entry_get(entry, rhythmdb.PROP_LOCATION)
  99.             if uri.startswith("file://"):
  100.                 location = unicode(urllib.unquote(uri[len("file://"):]))
  101.  
  102.                 # add a fake resource for the moment
  103.                 res = DIDLLite.Resource(location, 'http-get:*:%s:*' % mimetype)
  104.                 if size > 0:
  105.                     res.size = size
  106.                 if self.duration > 0:
  107.                     res.duration = self.duration
  108.                 if bitrate > 0:
  109.                     res.bitrate = str(bitrate)
  110.                 item.res.append(res)
  111.  
  112.             elt = DIDLLite.DIDLElement()
  113.             elt.addItem(item)
  114.             self.metadata = elt.toString()
  115.             self.entry = entry
  116.             if self.server != None:
  117.                 self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',self.metadata)
  118.                 self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',self.metadata)
  119.         if self.server != None:
  120.             self.server.av_transport_server.set_variable(connection_id, 'RelativeTimePosition', '00:00:00')
  121.             self.server.av_transport_server.set_variable(connection_id, 'AbsoluteTimePosition', '00:00:00')
  122.  
  123.     def playing_changed(self, player, state):
  124.         if state is True:
  125.             transport_state = 'PLAYING'
  126.         else:
  127.             if self.playing is False:
  128.                 transport_state = 'STOPPED'
  129.             else:
  130.                 transport_state = 'PAUSED_PLAYBACK'
  131.         self.update(transport_state)
  132.         try:
  133.             position = player.get_playing_time()
  134.         except:
  135.             position = None
  136.         try:
  137.             duration = player.get_playing_song_duration()
  138.         except:
  139.             duration = None
  140.         self.update_position(position,duration)
  141.  
  142.     def elapsed_changed(self, player, time):
  143.         try:
  144.             duration = player.get_playing_song_duration()
  145.         except:
  146.             duration = None
  147.         self.update_position(time,duration)
  148.  
  149.     def update(self, state):
  150.  
  151.         if state in ('STOPPED','READY'):
  152.             transport_state = 'STOPPED'
  153.         if state == 'PLAYING':
  154.             transport_state = 'PLAYING'
  155.         if state == 'PAUSED_PLAYBACK':
  156.             transport_state = 'PAUSED_PLAYBACK'
  157.  
  158.         if self.state != transport_state:
  159.             self.state = transport_state
  160.             if self.server != None:
  161.                 connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id)
  162.                 self.server.av_transport_server.set_variable(connection_id,
  163.                                                              'TransportState',
  164.                                                              transport_state)
  165.  
  166.  
  167.     def update_position(self, position,duration):
  168.         if self.server != None:
  169.             connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id)
  170.             self.server.av_transport_server.set_variable(connection_id, 'CurrentTrack', 0)
  171.  
  172.         if position is not None:
  173.             m,s = divmod( position, 60)
  174.             h,m = divmod(m,60)
  175.             if self.server != None:
  176.                 self.server.av_transport_server.set_variable(connection_id, 'RelativeTimePosition', '%02d:%02d:%02d' % (h,m,s))
  177.                 self.server.av_transport_server.set_variable(connection_id, 'AbsoluteTimePosition', '%02d:%02d:%02d' % (h,m,s))
  178.  
  179.         if duration <= 0:
  180.             duration = None
  181.  
  182.         if duration is not None:
  183.             m,s = divmod( duration, 60)
  184.             h,m = divmod(m,60)
  185.  
  186.             if self.server != None:
  187.                 self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackDuration', '%02d:%02d:%02d' % (h,m,s))
  188.                 self.server.av_transport_server.set_variable(connection_id, 'CurrentMediaDuration', '%02d:%02d:%02d' % (h,m,s))
  189.  
  190.             if self.duration is None:
  191.                 if self.metadata is not None:
  192.                     elt = DIDLLite.DIDLElement.fromString(self.metadata)
  193.                     for item in elt:
  194.                         for res in item.findall('res'):
  195.                             res.attrib['duration'] = "%d:%02d:%02d" % (h,m,s)
  196.                     self.metadata = elt.toString()
  197.  
  198.                     if self.server != None:
  199.                         self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',self.metadata)
  200.                         self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',self.metadata)
  201.  
  202.                 self.duration = duration
  203.  
  204.     def load( self, uri, metadata):
  205.         #self.shell.load_uri(uri,play=False)
  206.         self.duration = None
  207.         self.metadata = metadata
  208.         self.tags = {}
  209.  
  210.         was_playing = self.playing
  211.  
  212.         if was_playing == True:
  213.             self.stop()
  214.  
  215.         if len(metadata)>0:
  216.             elt = DIDLLite.DIDLElement.fromString(metadata)
  217.             if elt.numItems() == 1:
  218.                 item = elt.getItems()[0]
  219.  
  220.                 if uri.startswith('track-'):
  221.                     self.entry = self.shell.props.db.entry_lookup_by_id(int(uri[6:]))
  222.                 else:
  223.                     self.entry = self.shell.props.db.entry_lookup_by_location(uri)
  224.                 if self.entry == None:
  225.                     if item.server_uuid is not None:
  226.                         entry_type = self.shell.props.db.entry_register_type("CoherenceUpnp:" + item.server_uuid)
  227.                         self.entry = self.shell.props.db.entry_new(entry_type, uri)
  228.                     else:
  229.                         entry_type = self.shell.props.db.entry_register_type("CoherencePlayer")
  230.                         self.entry = self.shell.props.db.entry_new(entry_type, uri)
  231.  
  232.                 duration = None
  233.                 size = None
  234.                 bitrate = None
  235.                 for res in item.res:
  236.                     if res.data == uri:
  237.                         duration = res.duration
  238.                         size = res.size
  239.                         bitrate = res.bitrate
  240.                         break
  241.  
  242.                 self.shell.props.db.set(self.entry, rhythmdb.PROP_TITLE, item.title)
  243.                 try:
  244.                     if item.artist is not None:
  245.                         self.shell.props.db.set(self.entry, rhythmdb.PROP_ARTIST, item.artist)
  246.                 except AttributeError:
  247.                     pass
  248.                 try:
  249.                     if item.album is not None:
  250.                         self.shell.props.db.set(self.entry, rhythmdb.PROP_ALBUM, item.album)
  251.                 except AttributeError:
  252.                     pass
  253.  
  254.                 try:
  255.                     self.info("%r %r", item.title,item.originalTrackNumber)
  256.                     if item.originalTrackNumber is not None:
  257.                         self.shell.props.db.set(self.entry, rhythmdb.PROP_TRACK_NUMBER, int(item.originalTrackNumber))
  258.                 except AttributeError:
  259.                     pass
  260.  
  261.                 if duration is not None:
  262.                     h,m,s = duration.split(':')
  263.                     seconds = int(h)*3600 + int(m)*60 + int(s)
  264.                     self.info("%r %r:%r:%r %r", duration, h, m , s, seconds)
  265.                     self.shell.props.db.set(self.entry, rhythmdb.PROP_DURATION, seconds)
  266.  
  267.                 if size is not None:
  268.                     self.shell.props.db.set(self.entry, rhythmdb.PROP_FILE_SIZE,int(size))
  269.  
  270.         else:
  271.             if uri.startswith('track-'):
  272.                 self.entry = self.shell.props.db.entry_lookup_by_id(int(uri[6:]))
  273.             else:
  274.                 #self.shell.load_uri(uri,play=False)
  275.                 #self.entry = self.shell.props.db.entry_lookup_by_location(uri)
  276.                 entry_type = self.shell.props.db.entry_register_type("CoherencePlayer")
  277.                 self.entry = self.shell.props.db.entry_new(entry_type, uri)
  278.  
  279.  
  280.         self.playing = False
  281.         self.metadata = metadata
  282.  
  283.         connection_id = self.server.connection_manager_server.lookup_avt_id(self.current_connection_id)
  284.         self.server.av_transport_server.set_variable(connection_id, 'CurrentTransportActions','Play,Stop,Pause,Seek')
  285.         self.server.av_transport_server.set_variable(connection_id, 'NumberOfTracks',1)
  286.         self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackURI',uri)
  287.         self.server.av_transport_server.set_variable(connection_id, 'AVTransportURI',uri)
  288.         self.server.av_transport_server.set_variable(connection_id, 'AVTransportURIMetaData',metadata)
  289.         self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackURI',uri)
  290.         self.server.av_transport_server.set_variable(connection_id, 'CurrentTrackMetaData',metadata)
  291.  
  292.         if was_playing == True:
  293.             self.play()
  294.  
  295.     def start(self, uri):
  296.         self.load(uri)
  297.         self.play()
  298.  
  299.     def stop(self):
  300.         self.player.stop()
  301.         self.playing = False
  302.         #self.server.av_transport_server.set_variable( \
  303.         #    self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\
  304.         #                     'TransportState', 'STOPPED')
  305.  
  306.     def play(self):
  307.         if self.playing == False:
  308.             self.player.play_entry(self.entry)
  309.             self.playing = True
  310.         else:
  311.             self.player.playpause()
  312.         #self.server.av_transport_server.set_variable( \
  313.         #    self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\
  314.         #                     'TransportState', 'PLAYING')
  315.  
  316.     def pause(self):
  317.         self.player.pause()
  318.         #self.server.av_transport_server.set_variable( \
  319.         #    self.server.connection_manager_server.lookup_avt_id(self.current_connection_id),\
  320.         #                     'TransportState', 'PAUSED_PLAYBACK')
  321.  
  322.     def seek(self, location, old_state):
  323.         """
  324.         @param location:    simple number = time to seek to, in seconds
  325.                             +nL = relative seek forward n seconds
  326.                             -nL = relative seek backwards n seconds
  327.         """
  328.         self.player.seek(location)
  329.         self.server.av_transport_server.set_variable(0, 'TransportState', old_state)
  330.  
  331.     def mute(self):
  332.         self.muted_volume = self.volume
  333.         self.player.set_volume(0)
  334.         rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id)
  335.         self.server.rendering_control_server.set_variable(rcs_id, 'Mute', 'True')
  336.  
  337.     def unmute(self):
  338.         if self.muted_volume is not None:
  339.             self.player.set_volume(self.muted_volume)
  340.             self.muted_volume = None
  341.         self.player.set_mute(False)
  342.         rcs_id = self.server.connection_manager_server.lookup_rcs_id(self.current_connection_id)
  343.         self.server.rendering_control_server.set_variable(rcs_id, 'Mute', 'False')
  344.  
  345.     def get_mute(self):
  346.         return self.player.get_mute()
  347.  
  348.     def get_volume(self):
  349.         self.volume = self.player.get_volume()
  350.         return self.volume * 100
  351.  
  352.     def set_volume(self, volume):
  353.         volume = int(volume)
  354.         if volume < 0:
  355.             volume=0
  356.         if volume > 100:
  357.             volume=100
  358.  
  359.         self.player.set_volume(float(volume/100.0))
  360.  
  361.     def upnp_init(self):
  362.         self.player.connect ('playing-song-changed',
  363.                                  self.playing_song_changed),
  364.         self.player.connect ('playing-changed',
  365.                                  self.playing_changed)
  366.         self.player.connect ('elapsed-changed',
  367.                                  self.elapsed_changed)
  368.         self.player.connect("notify::volume", self.volume_changed)
  369.  
  370.         self.current_connection_id = None
  371.         self.server.connection_manager_server.set_variable(0, 'SinkProtocolInfo',
  372.                             ['rhythmbox:%s:audio/mpeg:*' % self.server.coherence.hostname,
  373.                              'http-get:*:audio/mpeg:*'],
  374.                             default=True)
  375.         self.server.av_transport_server.set_variable(0, 'TransportState', 'NO_MEDIA_PRESENT', default=True)
  376.         self.server.av_transport_server.set_variable(0, 'TransportStatus', 'OK', default=True)
  377.         self.server.av_transport_server.set_variable(0, 'CurrentPlayMode', 'NORMAL', default=True)
  378.         self.server.av_transport_server.set_variable(0, 'CurrentTransportActions', '', default=True)
  379.         self.server.rendering_control_server.set_variable(0, 'Volume', self.get_volume())
  380.         self.server.rendering_control_server.set_variable(0, 'Mute', self.get_mute())
  381.  
  382.     def upnp_Play(self, *args, **kwargs):
  383.         InstanceID = int(kwargs['InstanceID'])
  384.         Speed = int(kwargs['Speed'])
  385.         self.play()
  386.         return {}
  387.  
  388.     def upnp_Pause(self, *args, **kwargs):
  389.         InstanceID = int(kwargs['InstanceID'])
  390.         self.pause()
  391.         return {}
  392.  
  393.     def upnp_Stop(self, *args, **kwargs):
  394.         InstanceID = int(kwargs['InstanceID'])
  395.         self.stop()
  396.         return {}
  397.  
  398.     def upnp_Seek(self, *args, **kwargs):
  399.         InstanceID = int(kwargs['InstanceID'])
  400.         Unit = kwargs['Unit']
  401.         Target = kwargs['Target']
  402.         if Unit in ['ABS_TIME','REL_TIME']:
  403.             old_state = self.server.av_transport_server.get_variable(0, 'TransportState')
  404.             self.server.av_transport_server.set_variable(0, 'TransportState', 'TRANSITIONING')
  405.             h,m,s = Target.split(':')
  406.             seconds = int(h)*3600 + int(m)*60 + int(s)
  407.             self.seek(seconds, old_state)
  408.         return {}
  409.  
  410.     def upnp_SetAVTransportURI(self, *args, **kwargs):
  411.         InstanceID = int(kwargs['InstanceID'])
  412.         CurrentURI = kwargs['CurrentURI']
  413.         CurrentURIMetaData = kwargs['CurrentURIMetaData']
  414.         local_protocol_infos=self.server.connection_manager_server.get_variable('SinkProtocolInfo').value.split(',')
  415.         #print '>>>', local_protocol_infos
  416.         if len(CurrentURIMetaData)==0:
  417.             self.load(CurrentURI,CurrentURIMetaData)
  418.             return {}
  419.         else:
  420.             elt = DIDLLite.DIDLElement.fromString(CurrentURIMetaData)
  421.             #import pdb; pdb.set_trace()
  422.             if elt.numItems() == 1:
  423.                 item = elt.getItems()[0]
  424.                 res = item.res.get_matching(local_protocol_infos, protocol_type='rhythmbox')
  425.                 if len(res) == 0:
  426.                     res = item.res.get_matching(local_protocol_infos)
  427.                 if len(res) > 0:
  428.                     res = res[0]
  429.                     remote_protocol,remote_network,remote_content_format,_ = res.protocolInfo.split(':')
  430.                     self.load(res.data,CurrentURIMetaData)
  431.                     return {}
  432.         return failure.Failure(errorCode(714))
  433.  
  434.     def upnp_SetMute(self, *args, **kwargs):
  435.         InstanceID = int(kwargs['InstanceID'])
  436.         Channel = kwargs['Channel']
  437.         DesiredMute = kwargs['DesiredMute']
  438.         if DesiredMute in ['TRUE', 'True', 'true', '1','Yes','yes']:
  439.             self.mute()
  440.         else:
  441.             self.unmute()
  442.         return {}
  443.  
  444.     def upnp_SetVolume(self, *args, **kwargs):
  445.         InstanceID = int(kwargs['InstanceID'])
  446.         Channel = kwargs['Channel']
  447.         DesiredVolume = int(kwargs['DesiredVolume'])
  448.         self.set_volume(DesiredVolume)
  449.         return {}
  450.